package pub.dsb.example.thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 * https://leetcode-cn.com/problems/fizz-buzz-multithreaded/
 * </p>
 *
 * @author Yi
 * @version V1.0.0
 * @date 2020/12/31 16:01
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2020/12/31 16:01
 * @modify reason: {方法名}:{原因}
 * ...
 * @since V1.0.0
 */
public class LC1195Test {
    public static void main(String[] args) {
        FizzBuzz fizzBuzz = new FizzBuzz(1);
        new Thread(() -> {
            try {
                fizzBuzz.fizz();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T-A").start();
        new Thread(() -> {
            try {
                fizzBuzz.buzz();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T-B").start();
        new Thread(() -> {
            try {
                fizzBuzz.fizzbuzz();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T-C").start();
        new Thread(() -> {
            try {
                fizzBuzz.number();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T-D").start();
    }
}

class FizzBuzz {

    private AtomicInteger counter = new AtomicInteger(1);
    private Semaphore onlyFizz = new Semaphore(1);
    private Semaphore onlyBuzz = new Semaphore(1);
    private Semaphore buzzAndFizz = new Semaphore(1);
    private Semaphore number = new Semaphore(1);
    private int n;

    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz() throws InterruptedException {
        while (n > counter.get()) {
            onlyFizz.acquire(1);
            if (counter.get() % 3 == 0 && counter.get() % 5 != 0) {
                if (counter.get() <= n) {
                    System.out.println("fizz");
                }
                counter.addAndGet(1);
                number.release(1);
            }
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz() throws InterruptedException {
        while (n > counter.get()) {
            onlyBuzz.acquire(1);
            if (counter.get() % 3 != 0 && counter.get() % 5 == 0) {
                if (counter.get() <= n) {
                    System.out.println("buzz");
                }
                counter.addAndGet(1);
                number.release(1);
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz() throws InterruptedException {
        while (n > counter.get()) {
            buzzAndFizz.acquire(1);
            if (counter.get() % 3 == 0 && counter.get() % 5 == 0) {
                if (counter.get() <= n) {
                    System.out.println("fizzbuzz");
                }
                counter.addAndGet(1);
                number.release(1);
            }
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number() throws InterruptedException {
        while (n >= counter.get()) {
            number.acquire(1);
            if (counter.get() % 3 != 0
                    && counter.get() % 5 != 0) {
                if (counter.get() <= n) {
                    System.out.println(counter.get());
                }
                counter.addAndGet(1);
                number.release(1);
            }
            if (counter.get() % 3 == 0 && counter.get() % 5 == 0) {
                buzzAndFizz.release(1);
            }
            if (counter.get() % 3 == 0) {
                onlyFizz.release(1);
            }
            if (counter.get() % 5 == 0) {
                onlyBuzz.release(1);
            }
        }
        onlyBuzz.release(1);
        onlyFizz.release(1);
        buzzAndFizz.release(1);
    }
}
